package io.netty.bootstrap;

import gl.java.network.transport.kcp.udp.UdpServerChildChannel;
import gl.java.network.transport.kcp.udp.UdpServerChildHandler;
import io.netty.channel.*;
import io.netty.util.AttributeKey;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;

@Slf4j
public class UdpServerBootstrapAcceptorHandler extends ChannelInboundHandlerAdapter {
    private static InternalLogger logger = InternalLoggerFactory.getInstance(UdpServerBootstrapAcceptorHandler.class);
    private final ChannelHandler childHandler;
    private final Map.Entry<ChannelOption<?>, Object>[] childOptions;
    private final Map.Entry<AttributeKey<?>, Object>[] childAttrs;


    public UdpServerBootstrapAcceptorHandler(
            final Channel channel, ChannelHandler childHandler,
            Map.Entry<ChannelOption<?>, Object>[] childOptions, Map.Entry<AttributeKey<?>, Object>[] childAttrs) {
        this.childHandler = childHandler;
        this.childOptions = childOptions;
        this.childAttrs = childAttrs;
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
        log.info("channelRegistered");
    }


    private static void forceClose(Channel child, Throwable t) {
        child.unsafe().closeForcibly();
        logger.warn("Failed to register an accepted channel: {}", child, t);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // still let the exceptionCaught event flow through the pipeline to give the user
        // a chance to do something with it
        ctx.fireExceptionCaught(cause);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        final UdpServerChildChannel udpChildChannel = (UdpServerChildChannel) msg;
        log.info("Acceptor.initChildChannel:{}" , udpChildChannel.remoteAddress());
        udpChildChannel.pipeline().addLast(new UdpServerChildHandler());
        udpChildChannel.pipeline().addLast(childHandler);
        UdpServerBootstrap.setChannelOptions(udpChildChannel, childOptions, logger);
        for (Map.Entry<AttributeKey<?>, Object> e : childAttrs) {
            udpChildChannel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
        }
        try {
            ctx.channel().eventLoop().register(udpChildChannel).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (!future.isSuccess()) {
                        forceClose(udpChildChannel, future.cause());
                    }
                }
            });
        } catch (Throwable t) {
            forceClose(udpChildChannel, t);
        }

    }

}